//
//  ModelArray.m
//
//  Created by Molon on 13-10-25.
//  Copyright (c) 2013年 Molon. All rights reserved.
//

#import "ModelArray.h"
#import "Model.h"
#import "MLKitDebug.h"
#import "MLClassPropManager.h"
#import "MLJSON.h"

@interface ModelArray()

@property (nonatomic, unsafe_unretained) Class modelClass;
@property (nonatomic, strong) NSMutableArray *array;

@end

@implementation ModelArray

- (id)init
{
    self = [super init];
    if (self) {
        self.modelClass = [self configureModelClass];
        NSAssert(self.modelClass&&[self.modelClass isSubclassOfClass:[Model class]],@"ModelClass无效");
    }
    return self;
}

- (Class)configureModelClass
{
    NSString *selfClassString = NSStringFromClass([self class]);
    if ([selfClassString hasSuffix:@"Array"]) {
        NSString *modelClassString = [selfClassString substringToIndex:selfClassString.length-5];
        Class modelClass = NSClassFromString(modelClassString);
        return modelClass;
    }
    return nil;
}

- (void)dealloc
{
}

+ (instancetype)turnObject:(NSArray*)array
{
    ModelArray *modelArray = [[self alloc]init];
    [modelArray turnObject:array];
    
    return modelArray;
}

//重新定义
- (void)turnObject:(NSArray*)array
{
    if (self.count>0) {
        [self removeAllObjects];
    }
    //遍历并且根据modelClass来处理内部数据
    for (id aModel in array) {
        if ([aModel isKindOfClass:[NSDictionary class]]) {
            [self addObject:[self.modelClass turnObject:aModel]];
        }else if([aModel isKindOfClass:self.modelClass]){
            [self addObject:aModel];
        }else{
            NSAssert(NO, @"array转换%@发现有不合适元素",NSStringFromClass([self class]));
        }
    }
}

#pragma mark - getter
- (NSArray *)array
{
	if (!_array) {
		_array = [NSMutableArray array];
	}
	return _array;
}

#pragma mark 一些NSArray的常用方法,里面调用了KVO的Array Accessors
- (NSInteger)count
{
    return (NSInteger)[self countOfArray];
}

- (id)objectAtIndex:(NSUInteger)index
{
    return [self objectInArrayAtIndex:index];
}

- (NSUInteger)indexOfObject:(id)anObject
{
    return [self.array indexOfObject:anObject];
}

- (BOOL)containsObject:(id)anObject
{
    return [self.array containsObject:anObject];
}

//使得可以使用[index]语法
- (id)objectAtIndexedSubscript:(NSUInteger)idx NS_AVAILABLE(10_8, 6_0)
{
	return [self.array objectAtIndexedSubscript:idx];
}

- (id)firstObject
{
    return [self.array firstObject];
}

- (id)lastObject
{
    return [self.array lastObject];
}


- (BOOL)isObjectValid:(id)anObject
{
    return [anObject isKindOfClass:self.modelClass];
}

- (void)addObject:(id)anObject
{
    [self insertObject:anObject atIndex:self.array.count];
}

- (void)insertObject:(id)anObject atIndex:(NSUInteger)index
{
    [self insertObject:anObject inArrayAtIndex:index];
}

- (void)removeObjectAtIndex:(NSUInteger)index
{
    [self removeObjectFromArrayAtIndex:index];
}

- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject
{
    [self replaceObjectInArrayAtIndex:index withObject:anObject];
}

#pragma mark - 其他的，一些方便常用的
- (void)removeLastObject
{
    [self removeObjectAtIndex:self.array.count-1];
}

- (void)removeAllObjects
{
    NSIndexSet *set = [[NSIndexSet alloc]initWithIndexesInRange:NSMakeRange(0, self.array.count)];
    [self removeArrayAtIndexes:set];
}

- (void)removeObject:(id)object
{
    NSIndexSet *set = [self.array indexesOfObjectsPassingTest: ^BOOL(id obj, NSUInteger idx, BOOL *stop)  {                     return [obj isEqual:object];
    }];
    
    [self removeArrayAtIndexes:set];
}

- (void)removeObjectsInArray:(NSArray *)otherArray
{
    //如果某个元素在otherArray数组中存在就记录
    NSIndexSet *set = [self.array indexesOfObjectsPassingTest: ^BOOL(id obj, NSUInteger idx, BOOL *stop)  {                     return [otherArray containsObject:obj];
    }];
    
    [self removeArrayAtIndexes:set];
}

- (void)removeObjectsAtIndexes:(NSIndexSet *)indexes
{
    [self removeArrayAtIndexes:indexes];
}

//使得可以使用[index]=??语法
- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx NS_AVAILABLE(10_8, 6_0)
{
    if (idx==self.count){
        //add
        [self addObject:obj];
        return;
    }
    [self replaceObjectAtIndex:idx withObject:obj];
}

- (void)addObjectsFromArray:(NSArray *)objects
{
    NSIndexSet *set = [[NSIndexSet alloc]initWithIndexesInRange:NSMakeRange(self.array.count, objects.count)];
    [self insertObjects:objects atIndexes:set];
}

- (void)insertObjects:(NSArray *)objects atIndexes:(NSIndexSet *)indexes
{
    [self insertArray:objects atIndexes:indexes];
}

#pragma mark KVO Array Accessors

//get 必须存在
- (NSUInteger)countOfArray
{
    return [self.array count];
}

- (id)objectInArrayAtIndex:(NSUInteger)idx
{
    return [self.array objectAtIndex:idx];
}

//set
- (void)insertObject:(id)anObject inArrayAtIndex:(NSUInteger)idx
{
    NSAssert([self isObjectValid:anObject], @"欲添加对象非%@元素",NSStringFromClass(self.modelClass));
    [(NSMutableArray*)self.array insertObject:anObject atIndex:idx];
}

- (void)removeObjectFromArrayAtIndex:(NSUInteger)idx
{
    [(NSMutableArray*)self.array removeObjectAtIndex:idx];
}

- (void)replaceObjectInArrayAtIndex:(NSUInteger)index
                         withObject:(id)anObject {
    NSAssert([self isObjectValid:anObject], @"欲替换对象非%@元素",NSStringFromClass(self.modelClass));
    [(NSMutableArray*)self.array replaceObjectAtIndex:index withObject:anObject];
}

- (void)insertArray:(NSArray *)array atIndexes:(NSIndexSet *)indexes {
    for (id object in array) {
        if (![self isObjectValid:object]) {
            NSAssert(NO, @"欲添加数组中包含非%@元素",NSStringFromClass(self.modelClass));
            return;
        }
    }
    [(NSMutableArray*)self.array insertObjects:array atIndexes:indexes];
}

- (void)removeArrayAtIndexes:(NSIndexSet *)indexes {
    [(NSMutableArray*)self.array removeObjectsAtIndexes:indexes];
}

#pragma mark Fast Enumeration implementation
/**
 *  此特性可让此类被for in
 *  本身NSMutableArray是不支持for in里进行元素的修改，而这里不，这里的话在for in进行元素的增删都会保证for in开始时候的所有元素都遍历到
 */
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained [])buffer count:(NSUInteger)len
{
	NSUInteger count = 0;
	
	if(state->state == 0)
	{
		state->mutationsPtr = &state->extra[0];
	}
	
	NSUInteger listCount = [self count];
	if(state->state < listCount)
	{
		state->itemsPtr = buffer;
		while((state->state < listCount) && (count < len))
		{
			buffer[count] = [self objectAtIndex:state->state];
			state->state++;
			count++;
		}
	}
	else
	{
		count = 0;
	}
	
	return count;
}

#pragma mark - description
- (NSString *)description
{
    NSString *description = [NSString stringWithFormat:@"\n%@\n[\n",NSStringFromClass([self class])];
    
    NSDictionary *propToTypes = [[MLClassPropManager manager]propToTypesOfClass:self.modelClass untilSuperClass:[Model class] ignoreUntilClass:YES];
    
    for (Model *model in self.array) {
        description = [description stringByAppendingFormat:@"%@\n",[model dictionaryWithValuesForKeys:[propToTypes allKeys]].description];
    }
    description = [description stringByAppendingString:@"]\n"];
    return description;
}

#pragma mark - copy
- (id)copyWithZone:(NSZone *)zone {
    ModelArray *modelArray = [[[self class] allocWithZone:zone] init];
    
    //先把自身的一些属性浅copy了
    NSDictionary *propToTypes = [[MLClassPropManager manager]propToTypesOfClass:[self class] untilSuperClass:[ModelArray class] ignoreUntilClass:YES];
    for (NSString *key in [propToTypes allKeys]) {
        if ([key isEqualToString:@"array"]) {
            continue;
        }
        
        id object = [self valueForKey:key];
        if (object) {
//            if ([object isKindOfClass:[Model class]]||[object isKindOfClass:[ModelArray class]]) {
            if ([object conformsToProtocol:@protocol(NSCopying)]) {
                [modelArray setValue:[object copy] forKey:key];
            }else{
                [modelArray setValue:object forKey:key];
            }
        }
    }
    
    //copy array，array里的元素是深拷贝
    NSMutableArray *newArray = [NSMutableArray array];
    for (Model *model in self.array) {
        [newArray addObject:[model copy]];
    }
    [modelArray setValue:newArray forKey:@"array"];
    
    return modelArray;
}

#pragma mark - other
- (NSArray*)arrayWithElementsOfModelObjectForElementKey:(NSString*)key isIgnoreNil:(BOOL)isIgnoreNil
{
    //经过测试，这种方式比KVC @unionOfObjects. 方式快
    NSMutableArray *array = [NSMutableArray array];
    for (id object in self.array) {
        id value = [object valueForKey:key];
        if (!value||[value isKindOfClass:[NSNull class]]) {
            if (isIgnoreNil) {
                continue;
            }else{
                //这个需要注意。
                value = [NSNull null];
            }
        }
        [array addObject:value];
    }
    return array;
}

//默认不忽略nil
- (NSArray*)arrayWithElementsOfModelObjectForElementKey:(NSString*)key
{
    return [self arrayWithElementsOfModelObjectForElementKey:key isIgnoreNil:NO];
}

//根据Model的key，和当前数组里key重复的元素删除
- (void)removeObjectsWithModelArray:(ModelArray*)modelArray byKey:(NSString*)key
{
    NSAssert([modelArray isMemberOfClass:[self class]], @"两者的类型必须一致");
    
    //找到key数组
    NSArray *modelArrayValues = [modelArray arrayWithElementsOfModelObjectForElementKey:key isIgnoreNil:NO];
    
    NSIndexSet *set = [self.array indexesOfObjectsPassingTest: ^BOOL(id obj, NSUInteger idx, BOOL *stop)  {
        id value = [obj valueForKey:key];
        if (!value||[value isKindOfClass:[NSNull class]]) {
            return [modelArrayValues containsObject:[NSNull null]];
        }
        
        return [modelArrayValues containsObject:value];
    }];
    
    [self removeArrayAtIndexes:set];
}

//根据key equal去重
- (void)removeDuplicateObjectsByKey:(NSString *)key
{
    NSMutableSet *tempValues = [NSMutableSet set];
    NSIndexSet *set = [self.array indexesOfObjectsPassingTest: ^BOOL(id obj, NSUInteger idx, BOOL *stop)  {
        id value = [obj valueForKey:key];
        if (!value||[value isKindOfClass:[NSNull class]]) {
            value = [NSNull null];
        }
        
        //之后还发现会添加到set里
        if ([tempValues containsObject:value]) {
            return YES;
        }
        
        //第一次发现会add，不添加到set里
        [tempValues addObject:value];
        return NO;
    }];
    
    [self removeObjectsAtIndexes:set];
}


- (NSArray*)convertedArrayWithIgnoreNil:(BOOL)ignoreNil
{
    NSMutableArray *array = [NSMutableArray arrayWithCapacity:self.array.count];
    for (Model *model in self.array) {
        [array addObject:[model convertedDictionaryWithIgnoreNil:ignoreNil]];
    }
    return array;
}


- (NSString *)JSONStringWithIgnoreNil:(BOOL)ignoreNil
{
    NSArray *array = [self convertedArrayWithIgnoreNil:ignoreNil];
    NSAssert([array isValidJSONObject], @"此ModelArray不可转换为JSON，请检查");
    
    return [array JSONString];
}
@end
